package com.zlc.workflow.core.instance.cmd;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zlc.common.common.constant.WorkflowConstant;
import com.zlc.common.core.process.model.entity.StoneTaskHistory;
import com.zlc.common.core.process.model.vo.StoneProcessInstanceHistoryVo;
import com.zlc.common.core.process.model.vo.StoneProcessInstanceVo;
import com.zlc.common.core.process.model.vo.StoneProcessVo;
import com.zlc.common.core.process.model.vo.StoneTaskHistoryVo;
import com.zlc.common.core.process.model.vo.StoneTaskVo;
import com.zlc.common.core.process.service.StoneProcessInstanceHistoryService;
import com.zlc.common.core.process.service.StoneProcessInstanceService;
import com.zlc.common.core.process.service.StoneProcessLogService;
import com.zlc.common.core.process.service.StoneProcessService;
import com.zlc.common.core.process.service.StoneTaskHistoryService;
import com.zlc.common.core.process.service.StoneTaskService;
import com.zlc.common.core.user.model.vo.MayanPlatUserVo;
import com.zlc.common.core.user.service.MayanPlatUserService;
import com.zlc.common.util.AssertUtils;
import com.zlc.common.util.BeanCopyUtils;
import com.zlc.workflow.constant.ProcessLogTypeEnum;
import com.zlc.workflow.core.instance.model.EventParam;
import com.zlc.workflow.core.instance.model.TerminationFlowParam;
import com.zlc.workflow.core.instance.model.WorkflowDetail;
import com.zlc.workflow.core.instance.model.WorkflowTask;
import com.zlc.workflow.core.instance.service.EventService;
import com.zlc.workflow.flow.NodeModel;
import com.zlc.workflow.flow.NodeUtils;
import com.zlc.workflow.flow.node.TaskNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static com.zlc.common.common.constant.WorkflowConstant.EVENT_PROCESS_TERMINATION;
import static com.zlc.common.common.constant.WorkflowConstant.TASK_OPERATE_TYPE_TERMINATION;
import static com.zlc.common.common.constant.WorkflowConstant.TASK_STATUS_COMPLETE;

@Slf4j
public class TerminationCmd extends AbstractCmd<WorkflowDetail> {


    @Autowired
    private MayanPlatUserService mayanPlatUserService;
    @Autowired
    private StoneProcessService stoneProcessService;
    @Autowired
    private StoneProcessInstanceService stoneProcessInstanceService;
    @Autowired
    private StoneProcessInstanceHistoryService stoneProcessInstanceHistoryService;
    @Autowired
    private StoneTaskService stoneTaskService;
    @Autowired
    private StoneTaskHistoryService stoneTaskHistoryService;
    @Autowired
    private StoneProcessLogService stoneProcessLogService;
    @Autowired
    private EventService eventService;

    private final TerminationFlowParam workflowParam;

    private final String account;

    private final  String processId;

    private final MayanPlatUserVo nowUser;

    private final String processInstanceId;

    private final String taskId;

    private final StoneTaskVo taskVo;

    private final StoneProcessInstanceVo instanceVo;

    private TaskNode taskNode;

    private final  WorkflowDetail result = new WorkflowDetail();

    private StoneProcessVo processVo;

    /**
     * 获取当前命令对象的所有带 @Autowired注解的属性，然后从spring上下文中获取到注入进去
     *
     */
    public TerminationCmd(Object params) {
        super(params);
        this.workflowParam = (TerminationFlowParam) params;
        this.processId = workflowParam.getProcessId();
        this.account = workflowParam.getAccount();
        this.processInstanceId = workflowParam.getProcessInstanceId();
        this.taskId = workflowParam.getTaskId();
        AssertUtils.isTrue(!ObjectUtils.isEmpty(processInstanceId),"流程实例ID缺失");
        AssertUtils.isTrue(!ObjectUtils.isEmpty(taskId),"流程任务ID缺失");
        result.setProcessInstanceId(processInstanceId);
        this.instanceVo = stoneProcessInstanceService.getByProcessInstanceId(processInstanceId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(instanceVo),"流程实例ID错误");
        this.taskVo = stoneTaskService.getByTaskId(taskId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(taskVo),"流程任务ID错误");

        AssertUtils.isTrue(!ObjectUtils.isEmpty(workflowParam.getAccount()),"操作人不可为空");
        this.nowUser = mayanPlatUserService.queryByUserAccount(workflowParam.getAccount());
        AssertUtils.isTrue(!ObjectUtils.isEmpty(this.nowUser),"操作人不存在");
        AssertUtils.isTrue(account.equals(taskVo.getActorId()),"无权操作");

        this.processVo = stoneProcessService.getByProcessId(processId);
        String content = NodeUtils.getProcessContent(processVo.getContent());
        this.taskNode = NodeUtils.getTaskNode(content, processId, taskVo.getNodeId());
    }

    @Override
    public WorkflowDetail execute() {
        //终止任务
        completeNowTask();
        //终止流程
        terminationInstance();
        loadWorkflowDetail();
        return this.result;
    }


    private void loadWorkflowDetail(){
        WorkflowTask details = BeanCopyUtils.copyAndInit(instanceVo,WorkflowTask.class);
        details.setProcessCode(processVo.getProcessCode());
        details.setProcessName(processVo.getProcessName());
        details.setInitAccount(instanceVo.getStarter());
        details.setInitUserName(instanceVo.getStarterName());
        this.result.setDetails(details);
    }

    /**
     * 终止流程
     */
    private void terminationInstance(){
        //终止流程
        stoneProcessInstanceService.deleteObjById(instanceVo.getId());
        StoneProcessInstanceHistoryVo historyVo = stoneProcessInstanceHistoryService.getByProcessInstanceId(instanceVo.getProcessInstanceId());
        if(!ObjectUtils.isEmpty(historyVo)){
            historyVo.setStatus(WorkflowConstant.INSTANCE_STATUS_TERMINATION);
            historyVo.setTerminationTime(new Date());
            stoneProcessInstanceHistoryService.saveVo(historyVo);
        }
    }


    private void completeNowTask(){
        List<StoneTaskVo> nowTaskVos = stoneTaskService.getByProcessInstanceId(this.processInstanceId);
        //保存历史任务
        LambdaQueryWrapper<StoneTaskHistory> wrapper = stoneTaskHistoryService.wrapper()
                .eq(StoneTaskHistory::getProcessInstanceId, processInstanceId)
                .eq(StoneTaskHistory::getStatus, WorkflowConstant.TASK_STATUS_PROCESSING);
        List<StoneTaskHistoryVo> taskHistoryVos = stoneTaskHistoryService.queryVoList(wrapper);
        for(StoneTaskHistoryVo historyVo :taskHistoryVos){
            historyVo.setExecuteTime(new Date());
            historyVo.setStatus(TASK_STATUS_COMPLETE);
            historyVo.setOperateType(TASK_OPERATE_TYPE_TERMINATION);
            historyVo.setActorId(account);
            historyVo.setActorName(nowUser.getUsername());
            historyVo.setSuggest("终止");
            if(!ObjectUtils.isEmpty(workflowParam.getSuggest())){
                historyVo.setSuggest("终止, 终止原因:"+workflowParam.getSuggest());
            }
            stoneTaskHistoryService.saveVo(historyVo);
        }
        //删除进行中任务
        if(!ObjectUtils.isEmpty(nowTaskVos)){
            List<String> ids = nowTaskVos.stream().map(StoneTaskVo::getId).collect(Collectors.toList());
            stoneTaskService.deleteObjByIds(ids);
        }
        //流程终止时，执行回调
        eventExecute(taskNode,EVENT_PROCESS_TERMINATION);
        //记录日志
        stoneProcessLogService.saveLog(processInstanceId,processId,taskVo.getNodeId(),taskVo.getNodeName(),taskVo.getTaskId(),
                ProcessLogTypeEnum.TERMINATION.getType(),this.account,"终止了流程，任务节点【"+taskNode.nodeName()+"】");
    }


    private void eventExecute(NodeModel nodeModel, String eventTime){
        log.info("流程{}，执行自动处理前置回调",processInstanceId);
        EventParam eventParam = new EventParam();
        eventParam.setProcessCode(processVo.getProcessCode());
        eventParam.setProcessId(processId);
        eventParam.setActorId(this.account);
        eventParam.setActorName(this.nowUser.getUsername());
        eventParam.setEventTime(eventTime);
        eventParam.setNodeId(nodeModel.nodeId());
        eventParam.setNodeName(nodeModel.nodeName());
        eventParam.setNodeType(nodeModel.nodeType().value());
        eventParam.setProcessInstanceId(instanceVo.getProcessInstanceId());
        eventService.execute(eventParam);
    }

}
